#include <windows.h>
#include <memory.h>
#include <time.h>
#include <fstream>
#include <stdarg.h>

#include <ballade/util/logger.h>

namespace Ballade
{
	
	namespace Util
	{
		using namespace std;
		
		Logger::Logger()
		{
			setLevel(DEBUG_);
			_os = &clog;
		}
		
		Logger::Logger(const string &logFile)
		{
			setLevel(DEBUG_);
			_os = new ofstream(logFile.c_str());
			UseStdout = false;
		}
		
		Logger::~Logger()
		{
		}
		
		void Logger::setLevel(const Level level)
		{
			Level_ = level;
		}
		
		void Logger::setOutputStream(ostream *os)
		{
			_os = os;
		}
		
		bool Logger::isDebugEnabled()	const {	return Level_ >= DEBUG_;	}
		bool Logger::isInfoEnabled()	const {	return Level_ >= INFO_;		}
		bool Logger::isWarnEnabled()	const {	return Level_ >= WARN_;		}
		bool Logger::isErrorEnabled()	const { return Level_ >= ERROR_;	}
		bool Logger::isFatalEnabled()	const { return Level_ >= FATAL_;	}
		
		void Logger::debug(const char *fmt, ...)
		{
			va_list ap;
			va_start(ap, fmt);	
			doLog(DEBUG_, fmt, ap);
			va_end(ap);
		}
		
		void Logger::info(const char *fmt, ...)
		{
			va_list ap;
			va_start(ap, fmt);
			doLog(INFO_, fmt, ap);
			va_end(ap);
		}
		
		void Logger::warn(const char *fmt, ...)
		{
			va_list ap;
			va_start(ap, fmt);
			doLog(WARN_, fmt, ap);
			va_end(ap);
		}
		
		
		void Logger::error(const char *fmt, ...)
		{
			va_list ap;
			va_start(ap, fmt);	
			doLog(ERROR_, fmt, ap);
			va_end(ap);
		}
		
		void Logger::fatal(const char *fmt, ...)
		{
			va_list ap;
			va_start(ap, fmt);	
			doLog(FATAL_, fmt, ap);
			va_end(ap);
		}
		
		void Logger::doLog(const Level level, const char *fmt, va_list ap)
		{
			if (Level_ > level) return;
			
			while(IsBusy)
			{
				Sleep(1);
			}
			IsBusy = true;
			
			memset(_buf, 0x00, MAX_LOG_LINE);
			if(_vsnprintf( _buf, MAX_LOG_LINE - 1, fmt, ap ) == -1 )
				cerr << "Logger::DoLog, buffer is too small!\n" ;
			std::string header = createHeader(level);
			
			*_os << header << _buf << endl;
			IsBusy = false;
		}
		
		std::string Logger::createHeader(const Level level)
		{
			std::string header = "";
			
			time_t rawtime;
			struct tm * timeinfo;
			char buffer [80];
			
			time ( &rawtime );
			timeinfo = localtime ( &rawtime );
			
			strftime(buffer, 256, "%Y-%m-%d %H:%M:%S", timeinfo);
			header += buffer;
			
			switch(level)
			{
			case DEBUG_:
				header += " DEBUG ";
				break;
			case INFO_:
				header += " INFO ";
				break;
			case WARN_:
				header += " WARN ";
				break;
			case ERROR_:
				header += " ERROR ";
				break;
			case FATAL_:
				header += " FATAL ";
				break;
			}
			return header;
		}
		
		Logger::Level Logger::Level_ = Logger::Level::DEBUG_;
		int Logger::ReferenceNum = 0;
		bool Logger::IsBusy = false;
		bool Logger::UseStdout = true;
		Logger* Logger::Instance = NULL;
		
		Logger &Logger::getInstance()
		{
			if(!Instance) Instance = new Logger();
			Logger::ReferenceNum++;
			return *Instance;
		}
		
		Logger &Logger::getInstance(const string &logFile)
		{
			if(!Instance) 
			{
				Instance = new Logger(logFile);
			}
			else
			{
				Instance->setOutputStream(new ofstream(logFile.c_str()));
			}
			Logger::ReferenceNum++;
			return *Instance;
		}
		
		void Logger::destroy()
		{
			if(!UseStdout && _os) delete _os;
			if(Instance) delete Instance;
		}
		
		void Logger::close()
		{
			Logger::ReferenceNum--;
			if(Logger::ReferenceNum <= 0 && Instance) 
			{
				if(!UseStdout) 
				{
					delete _os;
					_os = NULL;
				}
				delete Instance;
				Instance = NULL;
			}
		}
	}
}
